package com.example.literj.kt.plugins

import com.example.literj.kt.core.*
import com.example.literj.kt.core.Observable
import com.example.literj.kt.core.Observer
import com.example.literj.kt.exceptions.UndeliverableException
import com.example.literj.kt.functions.BiFunction
import com.example.literj.kt.functions.Supplier
import com.example.literj.kt.internal.util.ExceptionHelper
import java.util.*

class RxJavaPlugins {
    companion object {
        @JvmStatic
        @Volatile
        private var mLockdown = false

        @JvmStatic
        fun onError(th: Throwable?) {
            var error = th
            if (error == null) {
                error =
                    ExceptionHelper.createNullPointerException("onError called with a null Throwable.")
            } else {
                if (!isBug(th)) {
                    error = UndeliverableException(error)
                }
            }

            error.printStackTrace()
            uncaught(error)
        }

        @JvmStatic
        private fun isBug(th: Throwable?): Boolean {
            if (th is IllegalStateException) {
                return true
            }

            if (th is NullPointerException) {
                return true
            }

            if (th is IllegalArgumentException) {
                return true
            }

            return false
        }

        @JvmStatic
        private fun uncaught(th: Throwable) {
            val curThread = Thread.currentThread()
            val handler = curThread.uncaughtExceptionHandler
            handler?.uncaughtException(curThread, th)
        }

        @JvmStatic
        fun onSchedule(run: Runnable?): Runnable {
            Objects.requireNonNull(run, "run is null")
            return run!!
        }

        @JvmStatic
        fun <T> onSubscribe(
            source: Observable<T>,
            observer: Observer<T>
        ): Observer<T> {
            return observer
        }

        @JvmStatic
        fun <T> onSubscribe(
            source: Single<T>,
            observer: SingleObserver<in T>
        ): SingleObserver<in T> {
            return observer
        }

        @JvmStatic
        fun <T> onAssembly(source: Single<T>): Single<T> {
            return source
        }

        @JvmStatic
        fun <T, U, R> apply(f: BiFunction<T, U, R>, t: T, u: U): R {
            return try {
                f.apply(t, u)
            } catch (ex: Throwable) {
                throw ex
            }
        }

        @JvmStatic
        fun initScheduler(defaultScheduler: Supplier<Scheduler>): Scheduler {
            Objects.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null")
            return callRequireNonNull(defaultScheduler)
        }

        @JvmStatic
        fun initScheduler(defaultScheduler: Scheduler): Scheduler {
            Objects.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null")
            return defaultScheduler
        }

        fun callRequireNonNull( s: Supplier<Scheduler>): Scheduler {
            return try {
                Objects.requireNonNull(s.get(), "Scheduler Supplier result can't be null")
            } catch (ex: Throwable) {
                throw ExceptionHelper.wrapOrThrow(ex)
            }
        }
    }
}